

#include "vector.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

void die_print_help();
void parse_args(int, char**);
void print_args();
void load_vect();
void load_def_vect();
void make_func_table();
struct arg_opt get_arg(char *);

/* Test function decs. */
void add();
void sub();
void scale();
void magnitude();
void norm();
void project();
void cross();
void dot();
void angle();
void inverse();

/* Holds what the val of an arg is and if it is set. */
struct arg_opt {

  char arg_sub;
  char set;
  char *data;

};

/* These chars specify wether a test function should be called. *
 * By default they are all zero.                                *
 *  call_add       = 0
 *  call_sub       = 1
 *  call_scale     = 2
 *  call_magnitude = 3                                         
 *  call_norm      = 4
 *  call_cross     = 5
 *  call_project   = 6
 *  call_dot       = 7
 *  call_angle     = 8
 *  vect_file      = 9
 *  call_inv       = 10
                                                                */
/* This is a file for using non default vectors.                */
char *vect_file = NULL;
char func_calls[11];
void **func_table;

/* These are the vectors to be used in the library testing. */
vm_vector *a;
vm_vector *b;

vm_vector def_a;
vm_vector def_b;
vm_vector load_a;
vm_vector load_b;

int main(int argc, char *argv[]){

  printf("Len of argc: %d, first arg: %s\n", argc, argv[0]);
  if (argc <= 1){
    die_print_help();
  } else {
    parse_args(argc, argv);
    //print_args();
  }

  make_func_table();

  load_def_vect();
  if (func_calls[9]){
    fprintf(stderr, "Attempting to load vectors...\n");
    load_vect();
  }

  printf("Using these vectors:\n");
  vm_println(*a);
  vm_println(*b);

  printf("Begining testing:\n");
  int i = 0;
  for (; i < 11; i++){
    if (func_calls[i]){
      void (*function)() = func_table[i];
      function ();
    }
  }
  return 0;

}

void make_func_table(){

  func_table = (void **) malloc(sizeof(void *) * 9);
  func_table[0] = add;
  func_table[1] = sub;
  func_table[2] = scale;
  func_table[3] = magnitude;
  func_table[4] = norm;
  func_table[5] = cross;
  func_table[6] = project;
  func_table[7] = dot;
  func_table[8] = angle;
  func_table[9] = 0;
  func_table[10] = inverse;

}

void load_vect(){

  load_a.x = 0; load_a.y = 0; load_a.z = 0;
  load_b.x = 0; load_b.y = 0; load_b.z = 0;

  FILE *vector_file = fopen(vect_file, "r");
  printf("Loading vectors from file: %s\n", vect_file);

  int a_ret = vm_read(vector_file, &load_a);
  //printf("[load_vect] ");
  //vm_println(load_a);
  if (a_ret == 0){
    printf("Error reading vector file, vector a will be default.\n");
  } else {
    a = &load_a;
  }

  int b_ret = vm_read(vector_file, &load_b);
  //printf("[load_vect] ");
  //vm_println(load_b);
  if (b_ret == 0){
    printf("Error reading vector file, vector b will be default.\n");
  } else {
    b = &load_b;
  }

}

void load_def_vect(){
  
  def_a.x = 4; def_a.y = -5; def_a.z = 6;
  def_b.x = 3; def_b.y = 2; def_b.z = -9;
  a = &def_a;
  b = &def_b;

}

void parse_args(int argc, char **argv){

  int i = 1;

  for (; i < argc; i++){
    struct arg_opt parsed_arg = get_arg(argv[i]);
    if (parsed_arg.arg_sub == -1){
      printf("Illegal argument: '%s' Ignoring.\n", argv[i]);
    } else if (parsed_arg.arg_sub == 9){
      vect_file = parsed_arg.data;
      func_calls[parsed_arg.arg_sub] = parsed_arg.set;
    } else {
      func_calls[parsed_arg.arg_sub] = parsed_arg.set;
    }
  }

}

void print_args(){

  int i = 0;
  for (; i < 11; i++){
    printf("Argval: %d; Set: %d\n", i, func_calls[i]);
  }
  if (func_calls[11]){
    printf("Using vector file: %s\n", vect_file);
  }

}

char strarg_to_intarg(char *arg){

  if (strcmp(arg, "call_add") == 0){
    return 0;
  } else if (strcmp(arg, "call_sub") == 0){
    return 1;
  } else if (strcmp(arg, "call_scale") == 0){
    return 2;
  } else if (strcmp(arg, "call_magnitude") == 0){
    return 3;
  } else if (strcmp(arg, "call_norm") == 0){
    return 4;
  } else if (strcmp(arg, "call_cross") == 0){
    return 5;
  } else if (strcmp(arg, "call_project") == 0){
    return 6;
  } else if (strcmp(arg, "call_dot") == 0){
    return 7;
  } else if (strcmp(arg, "call_angle") == 0){
    return 8;
  } else if (strcmp(arg, "vect_file") == 0){
    return 9;
  } else if (strcmp(arg, "call_inv") == 0){
    return 10;
  } else return -1;
}

struct arg_opt get_arg(char *arg){

  struct arg_opt parsed_arg;
  char *equal_loc = strpbrk(arg, "=");

  if (equal_loc == NULL) {
    struct arg_opt error;
    error.arg_sub = -1;
    error.set = -1;
    return error;
  }
  /* Otherwise... */
  *equal_loc = 0;
  equal_loc++;
  // Now we have (sort of) arg = equal_loc
  parsed_arg.arg_sub = strarg_to_intarg(arg);
  if (parsed_arg.arg_sub == 9){
    parsed_arg.set = 1;
    parsed_arg.data = malloc(strlen(equal_loc));
    strcpy(parsed_arg.data, equal_loc);
  } else {
    parsed_arg.set = strcmp(equal_loc, "yes") == 0 ? 1 : 0;
  }
  return parsed_arg;

}

void die_print_help(){

  printf("Help coming soon...\nExit.\n");
  exit(1);

}

/* These are the test functions. They are called from an array of pointers. */

void add(){

  printf("<------------------------ Add ------------------------>\n");
  printf("Adding vectors a and b:\n");
  printf("   ");
  vm_println(*a);
  printf(" + ");
  vm_println(*b);
  printf("----------------------------------\n   ");
  vm_vector v;
  vm_add(*a, *b, &v);
  vm_println(v);
  printf("\n");

}

void sub(){
  
  printf("<------------------------ Sub ------------------------>\n");
  printf("Subtractioning vectors a and b:\n");
  printf("   ");
  vm_println(*a);
  printf(" - ");
  vm_println(*b);
  printf("----------------------------------\n   ");
  vm_vector v;
  vm_sub(*a, *b, &v);
  vm_println(v);
  printf("\n");

}

void scale(){

  printf("<----------------------- Scale ----------------------->\n");
  printf("Scaling a by 2:\n");
  vm_println(*a);
  vm_vector v;
  vm_scale(*a, 2, &v);
  vm_println(v);
  printf("\n");
  
}

void magnitude(){

  printf("<--------------------- Magnitude --------------------->\n");
  printf("Magnitude of: "); vm_println(*a);
  printf("    %f\n", vm_magnitude(*a));
  printf("Magnitude of: "); vm_println(*b);
  printf("    %f\n", vm_magnitude(*b));
  printf("\n");

}

void norm(){

  printf("<----------------------- Norm ------------------------>\n");
  printf("A: "); vm_println(*a);
  printf("B: "); vm_println(*b);

  vm_vector a_norm;
  vm_vector b_norm;
  printf("A norm: "); 
  vm_norm(*a, &a_norm);
  vm_println(a_norm);
  printf("B norm: "); 
  vm_norm(*b, &b_norm);
  vm_println(b_norm);
  printf("\n");

}

void project(){
  
  printf("<---------------------- Project ---------------------->\n");
  printf("A: "); vm_println(*a);
  printf("B: "); vm_println(*b);
  printf(" Projecting B onto A: ");
  vm_vector proj;
  vm_project(*b, *a, &proj);
  vm_println(proj);
  printf("\n");

}

void cross(){

  printf("<----------------------- Cross ----------------------->\n");
  printf("A: "); vm_println(*a);
  printf("B: "); vm_println(*b);
  printf(" Crossing A by B: ");
  vm_vector cross;
  cross.w = 0;
  vm_cross(*a, *b, &cross);
  vm_println(cross);
  printf("\n");

}

void dot(){

  printf("<-------------------------- Dot ---------------------->\n");
  printf("a . b:\n");
  vm_println(*a);
  vm_println(*b);
  double dot_prod;
  vm_dot(*a, *b, &dot_prod);
  printf("Dot product: %f\n", dot_prod);
  printf("\n");

}

void angle(){

  printf("<------------------------- Angle --------------------->\n");
  printf("A: "); vm_println(*a);
  printf("B: "); vm_println(*b);
  printf(" Angle betweeen A and B: %f\n", vm_angle(*a, *b));
  printf("\n");

}

void inverse(){

  printf("<------------------------ Inverse -------------------->\n");
  printf("A: ");
  vm_println(*a);
  vm_vector inv;
  vm_inverse(*a, &inv);
  printf("A inverse: ");
  vm_println(inv);
  printf("\n");

}
